#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
#include <mutex>
#include <cassert>
#include <jsoncpp/json/json.h>

#include "../comm/util.hpp"
#include "../comm/logger.hpp"
#include "../comm/httplib.hpp"
// #include "oj_model.hpp"
#include "oj_model.hpp"
#include "oj_view.hpp"

namespace ns_control{
    using namespace ns_model;
    using namespace ns_view;
    class Machine{
        public:
            std::string _ip;
            int _port;
            uint64_t _load;
            std::mutex *mtx;//mutex禁止拷贝
        public:
            Machine():_ip(""),_port(0),_load(0),mtx(nullptr){}
            ~Machine(){}
        public:
            void incLoad(){
                if(mtx) mtx->lock();
                _load++;
                if(mtx) mtx->unlock();
            }
            void decLoad(){
                if(mtx) mtx->lock();
                _load--;
                if(mtx) mtx->unlock();
            }
            void resetLoad(){
                if(mtx) mtx->lock();
                _load = 0;
                if(mtx) mtx->unlock();
            }
            uint64_t Load(int& _load){
                if (mtx) mtx->lock();
                _load = _load;
                if (mtx)mtx->unlock();

                return _load;
            }
    };
    const std::string service_machine = "./conf/service_machine.conf";
    class LoadBlance{
        private:
            std::vector<Machine> machines;
            std::vector<int> online;
            std::vector<int> offline;
            std::mutex mtx;
        public:
            LoadBlance()
            {
                if(LoadConf(service_machine)){
                    DLOG("加载编译运行主机成功")
                }
            }
         ~LoadBlance(){}
        public:
           bool LoadConf(const std::string& filename){
            std::ifstream in(filename);
            if(!in.is_open()){
                ELOG("主机文件打开失败，无法获取主机");
                return false;
            }
            std::string machine_info;
            while(std::getline(in,machine_info)){
                std::vector<std::string> tmp_machine;
                Machine m;
                size_t size = helper::StringUtil::stringSpilt(machine_info,":",tmp_machine);
                if(size != 2){
                    DLOG("获取主机信息失败");
                    return false;
                }
                m._ip = tmp_machine[0];
                m._port = std::stoi(tmp_machine[1]);
                m._load = 0;
                m.mtx = new std::mutex();
                online.push_back(machines.size());
                machines.push_back(m);
                machine_info.clear();
            }
            ILOG("已经完成全部主机信息的获取")
            in.close();
            return true;
           }
           bool SmartChoice(int*id,Machine** m){
            std::unique_lock<std::mutex> _lock(mtx);
            if(online.empty()){
                ELOG("当前所有主机已经离线,没有主机可以处理请求");
                return false;
            }
            (*id) = online[0];
            (*m) = &machines[online[0]];
            size_t min_load = machines[online[0]]._load;
            for(int i=1;i<online.size();i++){
                if(min_load > machines[online[i]]._load){
                    (*id) = online[i];
                    (*m)  = &machines[online[i]];
                    min_load = machines[online[i]]._load;
                }
            }
            return true;
           }
           void offlineMachine(int which){
            std::unique_lock<std::mutex> lock(mtx);
            for(auto i = online.begin();i != online.end();i++){
                if( *i == which){
                    machines[which].resetLoad();
                    online.erase(i);
                    offline.push_back(*i);
                    break;
                }
            }
           }
           void OnlineMachine()
           {
               //我们统一上线，后面统一解决
               mtx.lock();
               online.insert(online.begin(), offline.begin(), offline.end());
               offline.erase(offline.begin(), offline.end());
               mtx.unlock();
   
               ILOG("所有主机上线");
           }
           //for test
           void ShowMachines()
           {
                mtx.lock();
                std::cout << "当前在线主机列表: ";
                for(auto &id : online)
                {
                    std::cout << id << " ";
                }
                std::cout << std::endl;
                std::cout << "当前离线主机列表: ";
                for(auto &id : offline)
                {
                    std::cout << id << " ";
                }
                std::cout << std::endl;
                mtx.unlock();
           }
    };
    class Control{
    private:
        Model _model; //提供后台数据
        View _view;   //提供html渲染功能
        LoadBlance load_blance_; //核心负载均衡器
    public:
        void RecoveryMachine()
        {
            load_blance_.OnlineMachine();
        }
        bool getAllQuestions(std::string *html){
            bool ret = true;
            std::vector<Question> question_vc;
            if(_model.getAllQuestions(&question_vc)){
                std::sort(question_vc.begin(),question_vc.end(),[](const struct Question& q1,const struct Question& q2){
                return q1.number > q2.number;
                });
                _view.AllExpandHtml(question_vc,html);
            }
            else{
                *html = "获取题目列表失败";
                 ret = false;
            }
            return ret;
        }
        bool getOneQuestion(const std::string &number, std::string *html){
            bool ret = true;
            Question q;
            if(_model.getOneQuestion(number,&q)){
                _view.OneExpandHtml(q,html);
            }
            else{
                *html = "获取指定题目失败";
                 ret =false;
            }
            return ret;
        }
        // code: #include...
        // input: ""
        void Judge(const std::string &number, const std::string in_json, std::string *out_json){
            // 根据题目编号，拿到对应题目
            Question q;
            _model.getOneQuestion(number, &q);

            // 构建发送给偏移服务器的字符串
            Json::Value tmp_value; // 存储解析后的json字符串
            Json::Reader reader;
            reader.parse(in_json, tmp_value);
            // 开始构建编译服务器的json对象
            Json::Value compile_json;
            std::string code = tmp_value["code"].asCString();
            compile_json["input"] = tmp_value["input"];
            compile_json["code"] = code + "/" + q.tail;
            compile_json["cpu_limit"] = q.cpu_limit;
            compile_json["mem_limit"] = q.mem_limit;

            Json::FastWriter writer;
            std::string compile_str = writer.write(compile_json);
            while (true)
            {
                int id = 0;
                Machine *m;
                if (!load_blance_.SmartChoice(&id, &m))
                {
                    DLOG("选择主机失败");
                    break;
                }

                httplib::Client cli(m->_ip, m->_port);
                m->incLoad();
                if (auto res = cli.Post("/compile_run", compile_str, "application/json;charset=utf-8"))
                {
                    if (res->status == 200)
                    {
                        ILOG("编译运行成功");
                        (*out_json) = res->body;
                        m->decLoad();
                        break;
                    }
                    DLOG("请求主机主机[ip:%s  port:%s]成功，但编译运行失败", m->_ip, m->_port);
                    m->decLoad();
                }
                else
                {
                    DLOG("请求主机[ip:%s  port:%s]失败", m->_ip, m->_port);
                    load_blance_.offlineMachine(id);
                    m->resetLoad();
                }
            }
        }
    };
}